/*
 * Copyright 2012 Gregor Schauer
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package at.schauer.gregor.vienna.example.swing;

import at.schauer.gregor.vienna.annotation.Id;
import at.schauer.gregor.vienna.datastore.file.PropertiesFileDataStore;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.swing.*;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;

/**
 * This demo application shows how Swing components can be made stateful without the need of creating custom components
 * or using a configuration mechanism.
 * <p/>
 * Instead the framework is used to save the state to a file within the temp directory of the system. During startup if
 * a pre-saved configuration was found, the state of the components will be restored.<br/>
 * Currently the following components are supported:
 * <pre><table>
 *     <tr><th>Component</th><th>Supported state information</th></tr>
 *     <tr><td>JCheckBox</td><td>text and selected</td></tr>
 *     <tr><td>JComboBox</td><td>items and the selected index</td></tr>
 *     <tr><td>JPasswordField</td><td>password encrypted by a JCA provider (here Bouncy Castle)</td></tr>
 *     <tr><td>JProgressBar</td><td>orientation, minimum, maximum and current value</td></tr>
 *     <tr><td>JSlider</td><td>orientation, minimum, maximum and current value</td></tr>
 *     <tr><td>JTabbedPane</td><td>tab placement, policy and selected index as well as the tab titles</td></tr>
 *     <tr><td>JTextComponent</td><td>text</td></tr>
 *     <tr><td>JToggleButton</td><td>text and selected</td></tr>
 *     <tr><td>JFrame</td><td>width, height as well as x/y location of the window</td></tr>
 * </table></pre>
 * <p/>
 * The Spring framework is used to do the initialization stuff (see <tt>swing.demo.xml</tt>).
 * The bean {@code swingDemoSerializer} contains the whole logic to persist the window with all its relevant members.
 * <p/>
 * At the first glimpse this may look like ridiculous for the functionality.
 * However here are some reasons why this is a good solution:
 * <ul>
 * <li>Swing does not provide those features out of the box</li>
 * <li>The JComponents are coupled loosely and are created only if necessary which allows easy exchange</li>
 * <li>The code which creates the components is completely free from persistence stuff</li>
 * <li>The persistence logic can be changed or even removed without touching the GUI</li>
 * <li>Serialization aspects of custom components can be reused for other purposes and within other applications</li>
 * </ul>
 *
 * @author Gregor Schauer
 */
@Component
@Id(name = "user")
public class SwingExample extends JFrame {
	private static final ApplicationContext context = new GenericXmlApplicationContext("at/schauer/gregor/vienna/example/swing.beans.xml");
	@SuppressWarnings("unchecked")
	private static final PropertiesFileDataStore<SwingExample> dataStore = context.getBean(PropertiesFileDataStore.class);
	private JPanel formPanel;
	private JPanel widgetsPanel;

	private JTabbedPane tabbedPane;
	private JTextField formTextField;
	private JTextField formPasswordField;
	private JCheckBox formPasswordCheckBox;
	private JToggleButton widgetToggleButton;
	private JSlider widgetSlider;
	private JComboBox widgetComboBox;
	private JProgressBar widgetProgressBar;

	static {
		try {
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
		} catch (Exception e) {
			// should not happen
		}
	}

	public static void main(String... args) {
		SwingExample window = dataStore.load(SwingExample.class, System.getProperty("user.name"));
		if (window == null) {
			window = new SwingExample();
		}
		window.setVisible(true);
	}

	public SwingExample() {
		setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
		addWindowListener(new WindowAdapter() {
			@Override
			public void windowClosing(WindowEvent e) {
				try {
					dataStore.save(SwingExample.this);
				} catch (Exception ex) {
					ex.printStackTrace();
					JOptionPane.showMessageDialog(null, ex.getClass().getSimpleName() + ": " + ex.getMessage());
				}
			}
		});
	}

	@Override
	public void setVisible(boolean b) {
		// Initialize components
		initialize();

		// Set window state
		if (getWidth() == 0 || getHeight() == 0) {
			pack();
		}
		if (getX() == 0 && getY() == 0) {
			setLocationRelativeTo(null);
		}

		// Start the show
		super.setVisible(b);
	}

	private void initialize() {
		setContentPane(getTabbedPane());

		WindowHelper helper = context.getBean(WindowHelper.class);
		helper.progressBar = this.widgetProgressBar;
	}

	private JTabbedPane getTabbedPane() {
		if (tabbedPane == null) {
			tabbedPane = new JTabbedPane();
			tabbedPane.addTab("Form", getFormPanel());
			tabbedPane.addTab("Static", new JLabel("Place your advertisement here."));
			tabbedPane.addTab("Widgets", getWidgetsPanel());
		} else if (tabbedPane.getTabComponentAt(0) == null) {
			tabbedPane.setComponentAt(0, getFormPanel());
			tabbedPane.setComponentAt(1, new JLabel("Place your advertisement here."));
			tabbedPane.setComponentAt(2, getWidgetsPanel());
		}
		return tabbedPane;
	}

	private JComponent getFormPanel() {
		if (formPanel == null) {
			formPanel = new JPanel(new GridBagLayout());
			formPanel.add(new JLabel("Name: "), createConstraints(0, 0));
			formPanel.add(getFormTextField(), createConstraints(1, 0));
			formPanel.add(new JLabel("Password: "), createConstraints(0, 1));
			formPanel.add(getFormPasswordField(), createConstraints(1, 1));
			formPanel.add(new JLabel(" "), createConstraints(0, 2));
			formPanel.add(getFormPasswordCheckBox(), createConstraints(1, 2));
		}
		return formPanel;
	}

	private JTextField getFormTextField() {
		if (formTextField == null) {
			formTextField = new JTextField();
		}
		return formTextField;
	}

	private JTextField getFormPasswordField() {
		if (formPasswordField == null) {
			formPasswordField = new JPasswordField();
		}
		return formPasswordField;
	}

	private JCheckBox getFormPasswordCheckBox() {
		if (formPasswordCheckBox == null) {
			formPasswordCheckBox = new JCheckBox("Login automatically");
		}
		return formPasswordCheckBox;
	}

	private JComponent getWidgetsPanel() {
		if (widgetsPanel == null) {
			widgetsPanel = new JPanel(new GridBagLayout());
			widgetsPanel.add(getWidgetToggleButton(), createConstraints(0, 0));
			widgetsPanel.add(getWidgetSlider(), createConstraints(0, 1));
			widgetsPanel.add(getWidgetComboBox(), createConstraints(0, 2));
			widgetsPanel.add(getWidgetProgressBar(), createConstraints(0, 3));
		}
		return widgetsPanel;
	}

	private JComponent getWidgetToggleButton() {
		if (widgetToggleButton == null) {
			widgetToggleButton = new JToggleButton("Toggle me", false);
		}
		return widgetToggleButton;
	}

	private JComponent getWidgetSlider() {
		if (widgetSlider == null) {
			widgetSlider = new JSlider(SwingConstants.HORIZONTAL, 0, 10, 5);
		}
		return widgetSlider;
	}

	private JComponent getWidgetComboBox() {
		if (widgetComboBox == null) {
			widgetComboBox = new JComboBox(new String[]{"First", "Second", "Third", "Fourth", "Fifth"});
		}
		return widgetComboBox;
	}

	private static GridBagConstraints createConstraints(int x, int y) {
		GridBagConstraints constraints = new GridBagConstraints();
		constraints.gridx = x;
		constraints.gridy = y;
		constraints.fill = GridBagConstraints.BOTH;
		return constraints;
	}

	private JComponent getWidgetProgressBar() {
		if (widgetProgressBar == null) {
			widgetProgressBar = new JProgressBar(0, 100);
		}
		return widgetProgressBar;
	}

	@Component
	static class WindowHelper {
		protected JProgressBar progressBar;

		@Scheduled(fixedDelay = 1000)
		protected void step() {
			if (progressBar != null) {
				if (progressBar.getValue() >= progressBar.getMaximum()) {
					progressBar.setValue(progressBar.getMinimum());
				} else {
					progressBar.setValue(progressBar.getValue() + 1);
				}
			}
		}
	}
}
